package main

import (
	"fmt"
	"io"
	"math"
	"net/http"
	"os"
	"time"
)

// Go 函数可以存储在变量中
var (
	myFprintf = func(w io.Writer, format string, a ...interface{}) (int, error) {
		return fmt.Fprintln(w, format, a)
	}
)

func main() {

	/* 定义局部变量 */
	var a int = 100
	var b int = 200
	var ret int

	/* 调用函数并返回最大值 */
	ret = max(a, b)

	fmt.Printf("最大值是 : %d\n", ret)

	/* 声明函数变量 */
	getSquareRoot := func(x float64) float64 {
		return math.Sqrt(x)
	}

	fmt.Println(getSquareRoot(9))

	fmt.Println("many return value func")

	fmt.Println(swap("a", "b"))

	/* defer操作
	 * 延迟（defer）语句，延迟语句被用于执行一个函数调用，在这个函数之前，延迟语句返回
	 */

	c := 10
	d := 20

	defer fmt.Println(c)

	fmt.Println(d)

	// 函数也是一种数据类型

	func01 := add

	fmt.Println(func01)

	fmt.Println(func01(10, 20))

	// 匿名函数

	func() {
		fmt.Println("我是一个匿名函数。。")
	}()

	fun3 := func() {
		fmt.Println("我也是一个匿名函数。。")
	}
	fun3()

	// 定义带参数的匿名函数
	func(a, b int) {
		fmt.Println(a, b)
	}(1, 2)

	//定义带返回值的匿名函数
	res1 := func(a, b int) int {
		return a + b
	}(10, 20) //匿名函数调用了，将执行结果给res1
	fmt.Println(res1)

	// 回调函数
	res2 := oper(40, 20, add)
	fmt.Println(res2)

	// Go 函数可以存储在变量中
	// 匿名函数赋值给了一个名为 myFprintf 的变量，通过这个变量，便可以调用刚刚定义的匿名函数
	myFprintf(os.Stdout, "%s\n", "hello,go")
	fmt.Printf("%T\n", myFprintf)

	// 支持在函数内创建并通过返回值返回
	teardown := setup("demo")
	defer teardown()
	println("do some bussiness stuff")

	// 作为参数传入函数
	// 这里通过 AfterFunc 函数设置了一个 2 秒的定时器，并传入了时间到了后要执行的函数。这里传入的就是一个匿名函数
	time.AfterFunc(time.Second*2, func() {
		println("timer fired")
	})

	// 拥有自己的类型

	// 应用一：函数类型的妙用
	// http.ListenAndServe(":8080", http.HandlerFunc(greeting))

	// 调用高阶函数
	// 以高频乘数2为固定乘数的乘法函数
	timesTwo := partialTimes(2)
	// 以高频乘数3为固定乘数的乘法函数
	timesThree := partialTimes(3)
	// 以高频乘数4为固定乘数的乘法函数
	timesFour := partialTimes(4)

	// 10，等价于times(2, 5)
	fmt.Println(timesTwo(5))
	// 12，等价于times(2, 6)
	fmt.Println(timesTwo(6))
	// 15，等价于times(3, 5)
	fmt.Println(timesThree(5))
	// 18，等价于times(3, 6)
	fmt.Println(timesThree(6))
	// 20，等价于times(4, 5)
	fmt.Println(timesFour(5))
	// 24，等价于times(4, 6)
	fmt.Println(timesFour(6))

}

/**
 * times 函数用来进行两个整型数的乘法
 */
func times(x, y int) int {
	return x * y
}

/**
 * 没必要每次都传入这样的高频乘数了。那怎样能省去高频乘数的传入
 * partialTimes 的返回值是一个接受单一参数的函数，这个由 partialTimes 函数生成的匿名函数，使用了 partialTimes 函数的参数 x
 * partialTimes 实质上就是用来生成以 x 为固定乘数的、接受另外一个乘数作为参数的、闭包函数的函数.
 */
func partialTimes(x int) func(int) int {
	return func(y int) int {
		return times(x, y)
	}
}

/**
 * Go 函数是“一等公民”，也就是说，它拥有自己的类型。
 * 函数也可以被显式转型
 */
func greeting(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Welcome, Gopher!\n")
}

/**
 * 简单的一个函数，实现了参数+1的操作
 * 变量在内存中是存放于一定地址上的，修改变量实际是修改变量地址处的内存
 * 	1.传指针使得多个函数能操作同一个对象
 *  2.传指针比较轻量级 (8bytes),只是传内存地址，我们可以用指针传递体积大的结构体
 */
func add01(a *int) int {
	/* 修改了a的值 */
	*a = *a - 1
	return *a
}

/**
 * 支持在函数内创建并通过返回值返回
 * Go 函数不仅可以在函数外创建，还可以在函数内创建。而且由于函数可以存储在变量中，所以函数也可以在创建后，作为函数返回值返回
 */
func setup(task string) func() {
	println("do some setup stuff for", task)
	// 闭包
	return func() {
		println("do some teardown stuff for", task)
	}
}

/*
 * 一个函数可以返回多个值
 * 一个函数可以没有返回值，也可以有一个返回值，也可以有返回多个值
 **/
func swap(x string, y string) (string, string) {
	return y, x
}

func add(x, y int) int {
	return x + y
}

/**
 * 比较大小
 */
func max(a int, b int) int {
	var result int
	if a > b {
		result = a
	} else {
		result = b
	}
	return result
}

func oper(a, b int, fun func(int, int) int) int {
	fmt.Println(a, b, fun) //打印3个参数
	res := fun(a, b)
	return res
}
